`timescale 1ns / 1ps

import uvm_pkg::*;

`include "uvm_macros.svh"

//user tx
// class user_transaction extends uvm_sequence_item;
//     rand    byte user_data[];

//     constraint ip_pyload_const{
//             user_data.size>=4;
//             user_data.size<=1024;
//     }

// endclass
typedef byte byte_queue_t [$];
//gmii rx
class gmii_transaction extends uvm_sequence_item;
    rand    bit[47:0]target_mac_addr;
    rand    bit[47:0]source_mac_addr;
    rand    bit[15:0]ethernet_type;
    rand    byte pyload[];
    bit [31:0]ethernet_crc;

    constraint arp_pyload_const{
        target_mac_addr == 48'hff_ff_ff_ff_ff_ff;
        source_mac_addr == 48'hd4_93_90_25_10_a2;
        // ethernet_type == 16'h0806;
        // pyload.size==46;

    }

    `uvm_object_utils(gmii_transaction)

    function new(string name = "gmii_transaction");
        super.new(name);
    endfunction

    function automatic byte_queue_t getDataWithPre();
        int size = this.pyload.size + 26;
        byte_queue_t all_data;
       

        for (int i = 0; i < size; i++) begin
            if(i < 7)
                all_data.push_back(8'h55);
            else if(i < 8)
                all_data.push_back(8'hd5);
            else if(i < 14)
                all_data.push_back(this.target_mac_addr[47-(i-8)*8 -: 8]);
            else if(i < 20)
                all_data.push_back(this.source_mac_addr[47-(i-14)*8 -: 8]);
            else if(i < 22)
                all_data.push_back(ethernet_type[15-(i-20)*8 -: 8]);
            else if(i < 22 + this.pyload.size)
                all_data.push_back(pyload[i-22]);
            else  
                all_data.push_back(ethernet_crc[31-(i-22 - this.pyload.size)*8 -: 8]);
        end
        return all_data;
    endfunction

    function automatic calc_crc32();
        logic [31:0] crc = 32'hFFFFFFFF; // 初始化CRC值
        int data_len = this.pyload.size + 14; 
        byte_queue_t valid_data;
        byte temp;

        for (int i = 0; i < data_len; i++) begin
            if(i < 6)
                valid_data.push_back(target_mac_addr[47-i*8 -: 8]);
            else if(i < 12)
                valid_data.push_back(source_mac_addr[47-(i-6)*8 -: 8]);
            else if(i < 14)
                valid_data.push_back(ethernet_type[15-(i-12)*8 -: 8]);
            else  
               valid_data.push_back(this.pyload[i-14]);
        end

        for (int i = 0; i < data_len; i++) begin
            temp = valid_data.pop_front();
            crc = crc ^ {24'h000000,temp};
            for (int j = 0; j < 8; j++) begin
                if (crc[0]) begin
                    crc = (crc >> 1) ^ 32'hEDB88320; // CRC32多项式
                end else begin
                    crc = crc >> 1;
                end
            end
        end
        valid_data.delete();
        this.ethernet_crc = ~{crc[7:0],crc[15:8],crc[23:16],crc[31:24]};// 取反得到最终CRC值???
    endfunction

endclass

class eht_top_driver extends uvm_driver;
    
    `uvm_component_utils(eht_top_driver)

    // virtual axi_stream_if axi_stream_vif;
    virtual gmii_if gmii_vif;

    // user_transaction user_tx_trans;
    gmii_transaction gmii_tx_trans;

    function new(string name = "eht_top_driver", uvm_component parent = null);
        super.new(name, parent);
        `uvm_info("eht_top_driver","new is called",UVM_LOW)
    endfunction

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        `uvm_info("eht_top_driver","build_phase is called",UVM_LOW)
        // if(!uvm_config_db# (virtual axi_stream_if )::get(this, "", "axi_stream_vif",axi_stream_vif))
        //     `uvm_fatal("eht_top_driver", "virtual axi_stream_if must be set for axi_stream_vif!!!")
        if(!uvm_config_db# (virtual gmii_if )::get(this, "", "gmii_vif",gmii_vif))
            `uvm_fatal("eht_top_driver", "virtual axi_stream_if must be set for gmii_vif!!!")
        
    endfunction


    task main_phase(uvm_phase phase);
        //object maismch,if there is not raise,the task finish immdately
        phase.raise_objection(this);
        `uvm_info("eht_top_driver","main_phase is called",UVM_LOW)
        gmii_vif.gmii_valid <= 0;
        #100ns;
        run_dirver();
        phase.drop_objection(this);
    endtask

    task run_dirver();
        
        gmii_rx_pakage_arp();
        #100us;

        // gmii_rx_pakage_udp();
        #2us;
        gmii_rx_pakage_tcp_open_ack();
        #10us;
    endtask

    task gmii_rx_pakage_arp();
        gmii_transaction trans;
        
        trans = new();
        assert(trans.randomize());
                trans.ethernet_type=16'h0806;
                trans.pyload = new[46];
                
                trans.pyload[0] = 8'h00;
                trans.pyload[1] = 8'h01;

                trans.pyload[2] = 8'h08;
                trans.pyload[3] = 8'h00;

                trans.pyload[4] = 8'h06;
                trans.pyload[5] = 8'h04;

                trans.pyload[6] = 8'h00;
                trans.pyload[7] = 8'h01;

                trans.pyload[8] = 8'hd4;
                trans.pyload[9] = 8'h93;
                trans.pyload[10] = 8'h90;
                trans.pyload[11] = 8'h25;
                trans.pyload[12] = 8'h10;
                trans.pyload[13] = 8'ha2;

                trans.pyload[14] = 8'hc0;
                trans.pyload[15] = 8'ha8;
                trans.pyload[16] = 8'h07;
                trans.pyload[17] = 8'h64;

                trans.pyload[18] = 8'h00;
                trans.pyload[19] = 8'h00;
                trans.pyload[20] = 8'h00;
                trans.pyload[21] = 8'h00;
                trans.pyload[22] = 8'h00;
                trans.pyload[23] = 8'h00;

                trans.pyload[24] = 8'hc0;
                trans.pyload[25] = 8'ha8;
                trans.pyload[26] = 8'h07;
                trans.pyload[27] = 8'h0b;

                trans.pyload[28] = 8'h00;
                trans.pyload[29] = 8'h00;
                trans.pyload[30] = 8'h00;
                trans.pyload[31] = 8'h00;
                trans.pyload[32] = 8'h00;
                trans.pyload[33] = 8'h00;
                trans.pyload[34] = 8'h00;
                trans.pyload[35] = 8'h00;
                trans.pyload[36] = 8'h00;
                trans.pyload[37] = 8'h00;
                trans.pyload[38] = 8'h00;
                trans.pyload[39] = 8'h00;
                trans.pyload[40] = 8'h00;
                trans.pyload[41] = 8'h00;
                trans.pyload[42] = 8'h00;
                trans.pyload[43] = 8'h00;
                trans.pyload[44] = 8'h00;
                trans.pyload[45] = 8'h00;
        trans.calc_crc32();
        drive_one_gmii_packet(trans);
    endtask


    task gmii_rx_pakage_icmp();
        gmii_transaction trans;
        
        trans = new();
        assert(trans.randomize());
                trans.ethernet_type=16'h0800;
                trans.pyload = new[60];
                trans.pyload[0] = 8'h45;
                trans.pyload[1] = 8'h00;

                trans.pyload[2] = 8'h00;
                trans.pyload[3] = 8'h3c;

                trans.pyload[4] = 8'hee;
                trans.pyload[5] = 8'hae;

                trans.pyload[6] = 8'h00;
                trans.pyload[7] = 8'h00;

                trans.pyload[8] = 8'h80;
                trans.pyload[9] = 8'h01;
                trans.pyload[10] = 8'h00;
                trans.pyload[11] = 8'h00;
                trans.pyload[12] = 8'hc0;
                trans.pyload[13] = 8'ha8;
                trans.pyload[14] = 8'h07;
                trans.pyload[15] = 8'h64;

                trans.pyload[16] = 8'hc0;
                trans.pyload[17] = 8'ha8;
                trans.pyload[18] = 8'h07;
                trans.pyload[19] = 8'h0b;

                trans.pyload[20] = 8'h80;
                trans.pyload[21] = 8'h00;
                trans.pyload[22] = 8'h4c;
                trans.pyload[23] = 8'h46;

                trans.pyload[24] = 8'h00;
                trans.pyload[25] = 8'h01;
                trans.pyload[26] = 8'h01;
                trans.pyload[27] = 8'h15;

                trans.pyload[28] = 8'h61;
                trans.pyload[29] = 8'h62;
                trans.pyload[30] = 8'h63;
                trans.pyload[31] = 8'h64;
                trans.pyload[32] = 8'h65;
                trans.pyload[33] = 8'h66;
                trans.pyload[34] = 8'h67;
                trans.pyload[35] = 8'h68;
                trans.pyload[36] = 8'h69;
                trans.pyload[37] = 8'h6a;
                trans.pyload[38] = 8'h6b;
                trans.pyload[39] = 8'h6c;
                trans.pyload[40] = 8'h6d;
                trans.pyload[41] = 8'h6e;
                trans.pyload[42] = 8'h6f;
                trans.pyload[43] = 8'h70;
                trans.pyload[44] = 8'h71;
                trans.pyload[45] = 8'h72;
                trans.pyload[46] = 8'h73;
                trans.pyload[47] = 8'h74;
                trans.pyload[48] = 8'h75;
                trans.pyload[49] = 8'h76;
                trans.pyload[50] = 8'h77;
                trans.pyload[51] = 8'h61;
                trans.pyload[52] = 8'h62;
                trans.pyload[53] = 8'h63;
                trans.pyload[54] = 8'h64;
                trans.pyload[55] = 8'h65;
                trans.pyload[56] = 8'h66;
                trans.pyload[57] = 8'h67;
                trans.pyload[58] = 8'h68;
                trans.pyload[59] = 8'h69;
        trans.calc_crc32();
        drive_one_gmii_packet(trans);
    endtask

    task gmii_rx_pakage_udp();
        gmii_transaction trans;
        
        trans = new();
        assert(trans.randomize());
                trans.ethernet_type=16'h0800;
                trans.pyload = new[46];
                trans.pyload[0] = 8'h45;
                trans.pyload[1] = 8'h00;

                trans.pyload[2] = 8'h00;
                trans.pyload[3] = 8'h2e;

                trans.pyload[4] = 8'hde;
                trans.pyload[5] = 8'h9f;

                trans.pyload[6] = 8'h00;
                trans.pyload[7] = 8'h00;

                trans.pyload[8] = 8'h80;
                trans.pyload[9] = 8'h11;
                trans.pyload[10] = 8'h00;
                trans.pyload[11] = 8'h00;
                trans.pyload[12] = 8'hc0;
                trans.pyload[13] = 8'ha8;
                trans.pyload[14] = 8'h07;
                trans.pyload[15] = 8'h64;

                trans.pyload[16] = 8'hc0;
                trans.pyload[17] = 8'ha8;
                trans.pyload[18] = 8'h07;
                trans.pyload[19] = 8'h0b;
                trans.pyload[20] = 8'h13;
                trans.pyload[21] = 8'h89;
                trans.pyload[22] = 8'h13;
                trans.pyload[23] = 8'h89;

                trans.pyload[24] = 8'h00;
                trans.pyload[25] = 8'h12;
                trans.pyload[26] = 8'h00;
                trans.pyload[27] = 8'h00;

                trans.pyload[28] = 8'h00;
                trans.pyload[29] = 8'h01;
                trans.pyload[30] = 8'h02;
                trans.pyload[31] = 8'h03;
                trans.pyload[32] = 8'h04;
                trans.pyload[33] = 8'h05;
                trans.pyload[34] = 8'h06;
                trans.pyload[35] = 8'h07;
                trans.pyload[36] = 8'h08;
                trans.pyload[37] = 8'h09;
                trans.pyload[38] = 8'h0a;
                trans.pyload[39] = 8'h0b;
                trans.pyload[40] = 8'h0c;
                trans.pyload[41] = 8'h0d;
                trans.pyload[42] = 8'h0e;
                trans.pyload[43] = 8'h0f;
                trans.pyload[44] = 8'h10;
                trans.pyload[45] = 8'h11;
        trans.calc_crc32();
        drive_one_gmii_packet(trans);
    endtask



    task gmii_rx_pakage_tcp_open_ack();
        gmii_transaction trans;
        
        trans = new();
        assert(trans.randomize());
                trans.ethernet_type=16'h0800;
                trans.pyload = new[46];
                trans.pyload[0] = 8'h45;
                trans.pyload[1] = 8'h00;

                trans.pyload[2] = 8'h00;
                trans.pyload[3] = 8'h2c;

                trans.pyload[4] = 8'h74;
                trans.pyload[5] = 8'hb9;

                trans.pyload[6] = 8'h40;
                trans.pyload[7] = 8'h00;

                trans.pyload[8] = 8'h80;
                trans.pyload[9] = 8'h06;
                trans.pyload[10] = 8'h00;
                trans.pyload[11] = 8'h00;
                trans.pyload[12] = 8'hc0;
                trans.pyload[13] = 8'ha8;
                trans.pyload[14] = 8'h07;
                trans.pyload[15] = 8'h64;

                trans.pyload[16] = 8'hc0;
                trans.pyload[17] = 8'ha8;
                trans.pyload[18] = 8'h07;
                trans.pyload[19] = 8'h0b;
                trans.pyload[20] = 8'h13;
                trans.pyload[21] = 8'h89;
                trans.pyload[22] = 8'h13;
                trans.pyload[23] = 8'h89;

                trans.pyload[24] = 8'h16;
                trans.pyload[25] = 8'hcf;
                trans.pyload[26] = 8'h6a;
                trans.pyload[27] = 8'hea;

                trans.pyload[28] = 8'h00;
                trans.pyload[29] = 8'h00;
                trans.pyload[30] = 8'h32;
                trans.pyload[31] = 8'hbd;
                trans.pyload[32] = 8'h60;
                trans.pyload[33] = 8'h12;
                trans.pyload[34] = 8'hfc;
                trans.pyload[35] = 8'h00;
                trans.pyload[36] = 8'h8f;
                trans.pyload[37] = 8'hde;
                trans.pyload[38] = 8'h00;
                trans.pyload[39] = 8'h00;
                trans.pyload[40] = 8'h02;
                trans.pyload[41] = 8'h04;
                trans.pyload[42] = 8'h23;
                trans.pyload[43] = 8'h00;
                trans.pyload[44] = 8'h00;
                trans.pyload[45] = 8'h00;
        trans.calc_crc32();
        drive_one_gmii_packet(trans);
    endtask


    task drive_one_gmii_packet(gmii_transaction trans);
    
        byte_queue_t tx_data;
        if(trans != null) begin
            tx_data = trans.getDataWithPre();
            while (tx_data.size()>0) begin
                @(posedge gmii_vif.clk);
                gmii_vif.gmii_valid <= 1;
                gmii_vif.gmii_data <= tx_data.pop_front();
            end
            @(posedge gmii_vif.clk);
            gmii_vif.gmii_valid <= 0;

        end

        tx_data.delete();
            
    endtask
endclass




module tp_ethernet_top;
    
    
    logic                                clk                     ;
    logic                                rst                     ;
    logic  [7 : 0]                       gmii_txdata             ;
    logic                                gmii_txvalid            ; 
    logic                                rst_delay               ;

    gmii_if                             gmii_rx_if(clk,rst);
    axis_master_if                      axis_user_tx_if(clk,rst);
    axis_slave_if                       axis_user_rx_if(clk,rst);

    initial begin
        clk <= 0;
        forever begin
            #4ns;
            clk <= ~clk;
        end
    end

    initial begin
        rst <= 1;
        #100ns;
        @(posedge clk)
        #1ns;
        rst <= 0;
    end

    initial begin
        rst_delay <= 1;
        #2us;
        @(posedge clk)
        rst_delay <= 0;
    end





    initial begin
        // uvm_config_db# (virtual axi_stream_if )::set(null, "uvm_test_top","axi_stream_vif",axis_user_tx_if);
        uvm_config_db# (virtual gmii_if )::set(null, "uvm_test_top","gmii_vif",gmii_rx_if);
        run_test("eht_top_driver");
    end

    test_pattern #(
        .TEST_DATA_LEN                  ( 16'd1472                                )
    )test_pattern_inst(
      .i_clk                            ( clk                               ),
      .i_rst                            ( rst_delay                         ),
      .i_test_pattern                   ( 3                                 ),
      .m_axis_tvalid                    ( axis_user_tx_if.m_axis_tvalid       ),
      .m_axis_tdata                     ( axis_user_tx_if.m_axis_tdata        ),
      .m_axis_tready                    ( axis_user_tx_if.m_axis_tready       ),
      .m_axis_tlast                     ( axis_user_tx_if.m_axis_tlast        ),
      .m_axis_tuser                     ( axis_user_tx_if.m_axis_tuser        ),
      .s_axis_tvalid                    ( axis_user_rx_if.s_axis_tvalid       ),
      .s_axis_tdata                     ( axis_user_rx_if.s_axis_tdata        ),
      .s_axis_tlast                     ( axis_user_rx_if.s_axis_tlast        ),
      .s_axis_tuser                     ( axis_user_rx_if.s_axis_tuser        )
   );

    ethernet_top #( 
        .SOURCE_PORT                     ( 16'd5001                                     ),
        .SOURCE_IP                       ( {8'd192,8'd168,8'd7,8'd11}                   ),
        .SOURCE_IP_MASK                  ( {8'd255,8'd255,8'd255,8'd0}                  ),
        .SOURCE_MAC                      ( 48'h11_22_33_44_55_66                        ),
        .MAC_CRC_ON                      ( 1                                            )
    )ethernet_top_inst(
        .i_clk                           ( clk                                          ),
        .i_rst                           ( rst                                          ),           
        .i_config_req                    ( 0),
        .i_config_source_port            ( 0),
        .i_config_source_ip              ( 0),
        .i_config_source_ip_mask         ( 0),
        .i_config_source_mac             ( 0),
        .user_tx_s_axis_tvalid           ( axis_user_tx_if.m_axis_tvalid      ),
        .user_tx_s_axis_tdata            ( axis_user_tx_if.m_axis_tdata       ),
        .user_tx_s_axis_tready           ( axis_user_tx_if.m_axis_tready      ),
        .user_tx_s_axis_tlast            ( axis_user_tx_if.m_axis_tlast       ),
        .user_tx_s_axis_tuser            ( axis_user_tx_if.m_axis_tuser       ),
        .user_rx_m_axis_tvalid           ( axis_user_rx_if.s_axis_tvalid      ),
        .user_rx_m_axis_tdata            ( axis_user_rx_if.s_axis_tdata       ),
        .user_rx_m_axis_tlast            ( axis_user_rx_if.s_axis_tlast       ),
        .user_rx_m_axis_tuser            ( axis_user_rx_if.s_axis_tuser       ),
        .i_gmii_rxdata                   ( gmii_rx_if.gmii_data                     ),
        .i_gmii_rxvalid                  ( gmii_rx_if.gmii_valid                    ),
        .o_gmii_txdata                   ( gmii_txdata                                ),
        .o_gmii_txvalid                  ( gmii_txvalid                               )
   );


endmodule
